package com.chinda.common.filter;

import com.alibaba.fastjson.JSON;
import com.chinda.common.config.jwt.JWTAutoConfiguration;
import com.chinda.common.enums.MessageEnum;
import com.chinda.common.security.SysUserDetails;
import com.chinda.common.utils.R;
import com.chinda.common.utils.ResponseUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * JWT接口请求校验拦截器
 * 请求接口时会进入这里验证Token是否合法和过期
 *
 * @author Wang Chinda
 * @date 2020/7/11
 * @see
 * @since 1.0
 */
@Slf4j
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {

    private final JWTAutoConfiguration jwtAutoConfiguration;

    public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager, JWTAutoConfiguration jwtAutoConfiguration) {
        super(authenticationManager);
        this.jwtAutoConfiguration = jwtAutoConfiguration;
    }

    /**
     * 验证token是否合法和过期
     * @param request
     * @param response
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        // 获取请求头中JWT的token
        String tokenHeader = request.getHeader(jwtAutoConfiguration.getTokenHeader());
        // 判断token是否合规
        if (null != tokenHeader && tokenHeader.startsWith(jwtAutoConfiguration.getTokenPrefix())) {
            try {
                // 截去JWT前缀
                String token = tokenHeader.replace(jwtAutoConfiguration.getTokenPrefix(), "");
                // 解析JWT
                Claims claims = Jwts.parser()
                        .setSigningKey(jwtAutoConfiguration.getSecret())
                        .parseClaimsJws(token)
                        .getBody();
                // 获取用户名和用户id
                String username = claims.getSubject();
                String userId = claims.getId();
                if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(userId)) {
                    // 获取角色
                    List<GrantedAuthority> authorities = null;
                    String authority = claims.get("authorities").toString();
                    if (!StringUtils.isEmpty(authority)) {
                        List<Map<String, String>> authorityMap = JSON.parseObject(authority, List.class);
                        authorities = authorityMap.stream().map(role ->
                                new SimpleGrantedAuthority(role.get("authority"))).collect(Collectors.toList());
                    }
                    //组装参数
                    SysUserDetails sysUserDetails = new SysUserDetails();
                    sysUserDetails.setUsername(claims.getSubject());
                    sysUserDetails.setUserId(Long.parseLong(claims.getId()));
                    sysUserDetails.setAuthorities(authorities);
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(sysUserDetails, userId, authorities);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (ExpiredJwtException e) {
                log.error(MessageEnum.SYS_SECURITY_0006.getMsg(), e);
                ResponseUtil.outJson(response, R.error(MessageEnum.SYS_SECURITY_0006.getMsg()));
            } catch (Exception e) {
                log.error(MessageEnum.SYS_SECURITY_0005.getMsg(), e);
                ResponseUtil.outJson(response, R.error(MessageEnum.SYS_SECURITY_0006.getMsg()));
            }
        }
        filterChain.doFilter(request, response);
    }
}
